import { Form, useActionData, useFetcher, useLoaderData, useNavigation } from "@remix-run/react"
import { useTranslation } from "react-i18next";
import useNotification, { Notification } from "~/components/notification";
import React, { useEffect, useMemo, useState } from "react";
import { findAllPermission, PermissionModel } from "~/.server/models/permission";
import i18nServer from "~/i18n/i18n.server";
import { ActionFunctionArgs, LoaderFunctionArgs } from "@remix-run/node";
import { FormFieldError } from "~/components/form_validate";
import { createRole, deleteRole, findAllRoles, RoleModel, updateRole, zRole } from "~/.server/models/role";
import { z } from "zod";
import { Confirm } from "~/components/confirm";

export async function loader({ request }: LoaderFunctionArgs) {
  const roles = await findAllRoles();
  const permissions = await findAllPermission();
  const locale = await i18nServer.getLocale(request);
  return { roles, permissions, isZh: locale === "zh" }
}

export default function Authority() {
  const actionData = useActionData<typeof action>();
  const { roles, permissions, isZh } = useLoaderData<typeof loader>();

  const { t } = useTranslation();
  const navigation = useNavigation();
  const isSubmitting = navigation.formAction === "/admin/roles";
  const { notifications, addNotification } = useNotification();
  const [roleInfo, setRoleInfo] = useState<Partial<RoleModel>>({});

  const allPermissionIds = useMemo(() => {
    const ids = new Set<number>();
    permissions?.forEach((permission) => {
      ids.add(permission.id ?? 0);
      if (permission.children) {
        permission.children.forEach((child) => ids.add(child.id ?? 0));
      }
    });
    return ids;
  }, [permissions]);

  // init actionData message
  useEffect(() => {
    if (actionData?.message) {
      addNotification({ type: actionData?.code === 0 ? "success" : "error", msg: actionData.message });
    }
    if (actionData?.code === 0) {
      closeRoleDialog();
      // closeDeleteRoleDialog();
    }
  }, [actionData, addNotification]);

  const handleOpenRoleDialog = () => {
    const dialog = document.getElementById('dialog-role') as HTMLDialogElement;
    dialog.showModal();
  };
  const handleEditRoleDialog = (roleId: number) => {
    const role = roles.find(role => role.id === roleId)
    setRoleInfo({ ...role });

    const dialog = document.getElementById('dialog-role') as HTMLDialogElement;
    dialog.showModal();
  }
  const handleCloseRoleDialog = (event: React.FormEvent) => {
    event.preventDefault();
    closeRoleDialog();
  };
  const closeRoleDialog = () => {
    const dialog = document.getElementById('dialog-role') as HTMLDialogElement;
    dialog.close();
    setRoleInfo({});
    const checkbox = document.getElementById("select-all-permission") as HTMLInputElement | null;
    if (checkbox) {
      checkbox.checked = false;
    }
  };

  // select all permissions
  React.useEffect(() => {
    const checkbox = document.getElementById("select-all-permission") as HTMLInputElement | null;
    if (checkbox) {
      checkbox.indeterminate = true;
    }
  }, [])

  // 添加一个esc监听，由于dialog默认监听esc关闭窗口，但是并不会清空数据，此处清空数据
  React.useEffect(() => {
    const handleEsc = (event: KeyboardEvent) => {
      if (event.key === "Escape") {
        closeRoleDialog();
      }
    };
    document.addEventListener("keydown", handleEsc);
    return () => {
      document.removeEventListener("keydown", handleEsc);
    };
  }, []);

  const handlePermissionCheck = (e: React.ChangeEvent<HTMLInputElement>, permissionId: number) => {
    const checked = e.target.checked;
    if (checked) {
      setRoleInfo(prev => {
        const newObj = { ...prev, permissionIds: new Set(prev.permissionIds) }
        if (!newObj.permissionIds) {
          newObj.permissionIds = new Set()
        }
        newObj.permissionIds!.add(permissionId)

        return newObj;
      })
    } else {
      setRoleInfo(prev => {
        if (!prev.permissionIds) {
          return prev
        }
        const newObj = { ...prev, permissionIds: new Set(prev.permissionIds) }
        newObj.permissionIds.delete(permissionId)
        return newObj;
      })
    }
  };
  const handleFormChange = (event: React.ChangeEvent<HTMLInputElement>) => {
    const { name, value } = event.target;
    setRoleInfo({ ...roleInfo, [name]: value });
  };
  const handleSelectAllPermissions = (event: React.ChangeEvent<HTMLInputElement>) => {
    const checked = event.target.checked;
    if (checked) {
      setRoleInfo(prev => {
        return { ...prev, permissionIds: new Set(allPermissionIds) };
      })
    } else {
      setRoleInfo(prev => {
        return { ...prev, permissionIds: new Set() }
      })
    }
  };

  // deleting
  const deleteRoleFetcher = useFetcher<typeof action>();
  const [isDeleting, setIsDeleting] = useState<boolean>(false);
  const [deleteRoleId, setDeleteRoleId] = useState<number | undefined>();
  const handleDeleteRole = (roleId: number) => {
    setIsDeleting(true);
    setDeleteRoleId(roleId);
  }
  const onDeleteRole = async () => {
    if (deleteRoleId) {
      const formData = new FormData();
      formData.append("id", deleteRoleId.toString());
      formData.append("action", "delete");
      deleteRoleFetcher.submit(formData, { method: "post" });
      if (deleteRoleFetcher.data?.code === 0) {
        addNotification({ type: "info", msg: deleteRoleFetcher.data.message });
      } else if (deleteRoleFetcher.data?.message) {
        addNotification({ type: "warning", msg: deleteRoleFetcher.data?.message });
      }
    }
    setIsDeleting(false);
    setDeleteRoleId(undefined);
  }
  const onCancelDeleteRole = () => {
    setIsDeleting(false);
    setDeleteRoleId(undefined);
  }

  const renderRoles = () => {
    return roles.map(role => {
      return (
        <div key={role.id} className="card bg-base-100 shadow-xl">
          <div className="card-body">
            <div className="flex justify-between items-center">
              <p className="text-sm text-gray-400">Total {role.users?.length} Users</p>
              {renderAvatar(role)}
            </div>
            <div className="flex justify-between items-center mt-8">
              <div className="flex-col float-start">
                <h2 className="card-title">{isZh ? role.zh_name ?? role.name : role.name}</h2>
                <button className="link link-primary link-hover font-light text-sm" onClick={() => handleEditRoleDialog(role.id!)}>{t('edit') + t('space') + t('role.name')}</button>
              </div>
              <button className="btn btn-ghost btn-circle btn-sm" onClick={() => handleDeleteRole(role.id!)}>
                <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor" className="size-5 text-error">
                  <path strokeLinecap="round" strokeLinejoin="round" d="m14.74 9-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 0 1-2.244 2.077H8.084a2.25 2.25 0 0 1-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 0 0-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 0 1 3.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 0 0-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 0 0-7.5 0" />
                </svg>
              </button>
            </div>
          </div>
        </div>
      )
    })
  }

  const renderAvatar = (role: RoleModel) => {
    if (role.users?.length || 0 > 0) {
      return (
        <div className="avatar-group -space-x-6 rtl:space-x-reverse">
          {role.users?.map((user, index) => {
            if (index < 3) {
              return (
                <div key={user.id} className="avatar">
                  <div className="w-10">
                    {
                      user.avatar ? (
                        // TODO 图片显示
                        <img src={user.avatar} alt={user.nickname} />
                      ) : (
                        <div className="bg-base-300 text-base-content w-full h-full rounded-full flex justify-center items-center">
                          <span className="text-xl">{user.username?.slice(0, 1)}</span>
                        </div>
                      )
                    }
                  </div>
                </div>
              )
            } else if (index === 3) {
              return (
                <div key={user.id} className="avatar placeholder">
                  <div className="bg-base-300 text-base-content w-10 rounded-full">
                    <span>+{(role.users?.length || 0) - 3}</span>
                  </div>
                </div>
              )
            }
          })}
        </div>
      )
    } else {
      return (
        <div className="avatar placeholder">
          <div className="bg-base-300 text-base-content w-10 rounded-full">
            <span>0</span>
          </div>
        </div>
      )
    }
  }

  const renderPermissionList = (permissions: PermissionModel[], level: number) => {
    return permissions.map(permission => {
      if (permission.path === "") {
        return (
          <React.Fragment key={permission.id}>
            <li className="flex justify-between items-center border-b pb-2">
              <div style={{ paddingLeft: level * 20 }}>{isZh ? permission.zh_name ?? permission.name : permission.name}</div>
              <div className="flex">
                {renderPermissionItem(permission.id!, t("read"))}
              </div>
            </li>
            {
              permission.children
              && permission.children.length > 0
              && (renderPermissionList(permission.children, level + 1))
            }
          </React.Fragment>
        )
      } else {
        return (
          <li key={permission.id} className="flex justify-between items-center border-b pb-2">
            <div style={{ paddingLeft: level * 20 }}>{isZh ? permission.zh_name ?? permission.name : permission.name}</div>
            <div className="flex">
              {renderPermissionItem(permission.id!, t("read"))}
              {permission.children && permission.children.length > 0 && (
                permission.children.map((child) => {
                  return (
                    <React.Fragment key={child.id}>
                      {renderPermissionItem(child.id!, isZh ? child.zh_name ?? child.name : child.name)}
                    </React.Fragment>
                  )
                })
              )}
            </div>
          </li>
        )
      }
    });
  }

  const renderPermissionItem = React.useMemo(() => {
    const PermissionItem = (permissionId: number, name?: string) => {
      return (
        <label className="cursor-pointer label gap-1">
          <input
            type="checkbox"
            className="checkbox checkbox-primary"
            name="permissionIds"
            value={permissionId}
            checked={roleInfo.permissionIds?.has(permissionId) ?? false}
            onChange={(e) => handlePermissionCheck(e, permissionId)}
          />
          <span className="label-text">{name}</span>
        </label>
      );
    };
    PermissionItem.displayName = 'PermissionItem';
    return PermissionItem;
  }, [roleInfo.permissionIds]);

  return (
    <>
      <div className="w-full grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 justify-items-stretch gap-4">
        {renderRoles()}
        <div className="card bg-base-100 shadow-xl">
          <div className="card-body">
            <h2 className="card-title">{t("add") + t("space") + t("role.name")}</h2>
            <p>{t("role.add_description")}</p>
            <div className="card-actions justify-end">
              <button className="btn btn-primary" onClick={handleOpenRoleDialog}>{t("add") + t("space") + t("role.name")}</button>
            </div>
          </div>
        </div>
      </div>

      {/* 添加或修改 */}
      <dialog id="dialog-role" className="modal">
        <div className="modal-box max-w-3xl">
          <h1 className="font-bold text-2xl text-center">{t("add") + t("space") + t("role.name")}</h1>
          <Form method="post">
            <input type="hidden" name="id" defaultValue={roleInfo.id ?? "0"} />
            <label className="form-control w-full">
              <div className="label">
                <span className="label-text after:content-['*'] after:ml-0.5 after:text-error">{t("name")}</span>
              </div>
              <input type="text" name="name" value={roleInfo.name ?? ""} onChange={handleFormChange} placeholder={t("name")} className={`input input-bordered w-full ${actionData?.name && "input-error"}`} />
              <FormFieldError msg={actionData?.name} />
            </label>
            <h2 className="text-lg my-8">Role Permissions</h2>
            <ul>
              <li className="flex justify-between items-center border-b pb-2">
                <div>{t("role.admin_access")}</div>
                <div className="flex">
                  <label className="cursor-pointer label gap-1">
                    <input type="checkbox" className="checkbox checkbox-primary" id="select-all-permission" onChange={handleSelectAllPermissions} />
                    <span className="label-text">{t("select_all")}</span>
                  </label>
                </div>
              </li>
              {renderPermissionList(permissions, 0)}
            </ul>
            <div className="modal-action">
              <button className={`btn btn-primary ${isSubmitting ? "btn-disabled" : ""}`}>
                {isSubmitting ? <span className="loading loading-spinner"></span> : ""}
                {t("save")}
              </button>
              <button className="btn" onClick={handleCloseRoleDialog}>{t("close")}</button>
            </div>
          </Form>
        </div>
      </dialog>
      <Notification notifications={notifications} />
      {isDeleting
        ? <Confirm
          isSubmitting={deleteRoleFetcher.state !== "idle"}
          message={t("confirmDelete") + " [" + deleteRoleId + "]"}
          onConfirm={onDeleteRole}
          onCancel={onCancelDeleteRole} />
        : ""}
    </>
  )
}

export async function action({ request }: ActionFunctionArgs) {
  const t = await i18nServer.getFixedT(request);
  const body = await request.formData();
  if (body.get("action") === "delete") {
    const r = await deleteRole(Number(body.get("id")));
    return {
      code: r ? 0 : 1,
      message: r ? t("delete") + t("space") + t("success") : t("role.has_users"),
    }
  }
  const validate = zRole.extend({
    name: z.string().min(1, { message: t("name") + t("space") + t("required") }),
  })
  const data = Object.fromEntries(body);
  data.permissionIds = JSON.stringify(body.getAll("permissionIds"));
  const result = validate.safeParse(data);
  if (!result.success) {
    const error = result.error.flatten();
    console.log(error.fieldErrors);
    return {
      ...error.fieldErrors,
      code: 1,
      message: t("something_wrong"),
    }
  }

  const model = { ...result.data };
  const code = model.id ? await updateRole(model) : await createRole(model);
  return {
    code: code,
    message: code === 1 ? "" : (model.id ? t("edit") : t("add")) + t("space") + t("success"),
    name: code === 1 ? t("role.repeat_name") : ""
  }
}